
package edu.ucla.cs.typecast.bloomfilter;

import java.io.IOException;
import java.io.InputStream;
import java.util.Arrays;


/**
 *  
 *  This is the class representing Bloom filter. 
 *
 */
public class BloomFilter {
    public static final int SIZE = 16; 
    
    private byte[] filter; 
    
    public BloomFilter(String hexString) { 
        filter = new byte[SIZE]; 
        for(int i=0; i<filter.length; i++) { 
            filter[i] = 0; 
        }
        for(int i = 0; i<hexString.length(); i += 2) { 
            int hi = getHexValue(hexString.charAt(i)); 
            int low = (i+1) < hexString.length() ? getHexValue(hexString.charAt(i+1)) : 0;              
            filter[i/2] = (byte)(hi << 4 | low); 
        }
    }

    public BloomFilter(byte[] buf) { 
    	this(buf, 0); 
    }
    
    public BloomFilter(byte[] buf, int start) { 
        filter = new byte[SIZE]; 
        for(int i=0; i<SIZE; i++) { 
            filter[i] = buf[i + start]; 
        }        
    }

    public BloomFilter() { 
    	filter = new byte[SIZE]; 
    	Arrays.fill(filter, (byte)0); 
    }
    
    public boolean isEmpty() { 
    	for(int i=0; i<SIZE; i++) { 
    		if (filter[i] != 0x00) { 
    			return false; 
    		}
    	}
    	return true; 
    }
    
    public void readFromStream(InputStream in) throws IOException { 
    	int len = in.read(filter, 0, SIZE); 
    	if (len != SIZE) { 
    		throw new IOException("Unexpected end of stream"); 
    	}
    }
    
    /**
     * Set a specific bit of the Bloom filter to 1. 
     * @param index
     */
    public void setBit(int index) { 
    	if (index >= SIZE * 8) { 
    		throw new RuntimeException("Out of index error"); 
    	}
    	int byteIndex = index / SIZE; 
    	int bitIndex =  index % 8; 
    	byte value = (byte)(0x01 << bitIndex);         
    	filter[byteIndex] |= value;     	
    }

    public byte[] getValue() { 
        return filter; 
    }
    
    public String toString() { 
        StringBuffer buf = new StringBuffer(); 
        for(int i=0; i<filter.length; i++) { 
            toHex(buf, filter[i]); 
        }
        return buf.toString(); 
    }
    
    private void toHex(StringBuffer buf, byte byteValue) { 
        int value = byteValue >> 4 & 0x0F; 
        char ch; 
        if (value <= 9) { 
            ch = (char)('0' + value); 
            buf.append(ch); 
        } else { 
            ch = (char)('A' + value - 10); 
            buf.append(ch); 
        }
        value = byteValue & 0x0F; 
        
        if (value <= 9) { 
            ch = (char)('0' + value); 
            buf.append(ch); 
        } else { 
            ch = (char)('A' + value - 10); 
            buf.append(ch); 
        }
        
    }
    private int getHexValue(char ch) { 
        if (ch >= '0' && ch <= '9') { 
            return  ch - '0';
        } else if (ch >= 'a' && ch <= 'f') { 
            return ch - 'a' + 10; 
        } else if (ch >= 'A' && ch <= 'F') { 
            return ch - 'A' + 10; 
        } else { 
            throw new RuntimeException(ch + " is not a HEX"); 
        }         
    }
    
    public static void main(String[] args) { 
        BloomFilter filter = new BloomFilter(args[0]); 
        System.out.println("filter=" + filter.toString()); 
    }
}
